package org.apache.maven.plugins.jars;

import org.apache.maven.archiver.MavenArchiveConfiguration;
import org.apache.maven.archiver.MavenArchiver;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.*;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.jar.JarArchiver;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * User: alexz
 * Date: 14-7-22
 * Time: 下午5:03
 * To change this template use File | Settings | File Templates.
 *
 * @goal jars
 */
@Mojo(name = "jars", defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true, threadSafe = true,
        requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
public class JarsMojo extends AbstractMojo {

    private static final String[] DEFAULT_EXCLUDES = new String[]{"**/package.html"};

    private static final String[] DEFAULT_INCLUDES = new String[]{"**/**"};

    @Component
    private MavenProjectHelper projectHelper;

    @Component(role = Archiver.class, hint = "jar")
    private JarArchiver jarArchiver;

    @Component
    private MavenProject project;

    @Component
    private MavenSession session;

    /**
     * List of files to include. Specified as fileset patterns which are relative to the input directory whose contents
     * is being packaged into the JAR.
     */
    @Parameter
    private String[] includes;

    /**
     * List of files to exclude. Specified as fileset patterns which are relative to the input directory whose contents
     * is being packaged into the JAR.
     */
    @Parameter
    private String[] excludes;

    /**
     * Path to the default MANIFEST file to use. It will be used if
     * <code>useDefaultManifestFile</code> is set to <code>true</code>.
     *
     * @since 2.2
     */
    @Parameter(defaultValue = "${project.build.outputDirectory}/META-INF/MANIFEST.MF", required = true,
            readonly = true)
    private File defaultManifestFile;

    /**
     * Directory containing the classes and resource files that should be packaged into the JAR.
     */
    @Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
    private File classesDirectory;

    @Parameter(defaultValue = "${project.build.directory}", required = true)
    private File outputDirectory;

    @Parameter(property = "jars-items", required = true)
    private List<JarsItem> jarsItems = new ArrayList<JarsItem>();

    @Override
    public void execute() throws MojoExecutionException {
        if ((!getClassesDirectory().exists() || getClassesDirectory().list().length < 1)) {
            getLog().info("Skipping packaging of the " + getType());
        } else {
            for (JarsItem jarsItem : jarsItems) {
                File jarFile = createArchive(jarsItem);
                projectHelper.attachArtifact(getProject(), getType(), jarFile);
            }
        }
    }

    protected final MavenProject getProject() {
        return project;
    }

    private File createArchive(JarsItem jarsItem) throws MojoExecutionException {
        String projectName = jarsItem.getProjectName();
        String mainClass = jarsItem.getMainClass();
        File dependenciesDir = jarsItem.getDependenciesDir();
        boolean useDefaultManifestFile = jarsItem.isUseDefaultManifestFile();

        File jarFile = getJarFile(outputDirectory, projectName, getClassifier());

        MavenArchiver archiver = new MavenArchiver();

        archiver.setArchiver(jarArchiver);

        archiver.setOutputFile(jarFile);

        MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
        archive.setForced(false);

        try {
            File contentDirectory = getClassesDirectory();
            if (!contentDirectory.exists()) {
                getLog().warn("JARS will be empty - no content was marked for inclusion!");
            } else {
                archiver.getArchiver().addDirectory(contentDirectory, getIncludes(), getExcludes());
            }

            File existingManifest = getDefaultManifestFile();

            if (useDefaultManifestFile && existingManifest.exists() && archive.getManifestFile() == null) {
                getLog().info("Adding existing MANIFEST to archive. Found under: " + existingManifest.getPath());
                archive.setManifestFile(existingManifest);
            }

            archiver.createArchive(session, project, archive);

            return jarFile;
        } catch (Exception e) {
            throw new MojoExecutionException("Error assembling JARS", e);
        }
    }

    /**
     * Default Manifest location. Can point to a non existing file.
     * Cannot return null.
     */
    protected File getDefaultManifestFile() {
        return defaultManifestFile;
    }


    protected static File getJarFile(File basedir, String finalName, String classifier) {
        if (classifier == null) {
            classifier = "";
        } else if (classifier.trim().length() > 0 && !classifier.startsWith("-")) {
            classifier = "-" + classifier;
        }

        return new File(basedir, finalName + classifier + ".jar");
    }


    private String[] getIncludes() {
        if (includes != null && includes.length > 0) {
            return includes;
        }
        return DEFAULT_INCLUDES;
    }

    private String[] getExcludes() {
        if (excludes != null && excludes.length > 0) {
            return excludes;
        }
        return DEFAULT_EXCLUDES;
    }

    protected File getClassesDirectory() {
        return classesDirectory;
    }

    protected String getClassifier() {
        return null;
    }

    protected String getType() {
        return "jars";
    }

    public List<JarsItem> getJarsItems() {
        return jarsItems;
    }
}
